package com.kzw.core.service;

import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.Query;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.kzw.core.orm.Page;
import com.kzw.core.orm.PageRequest;
import com.kzw.core.orm.PageRequest.Sort;
import com.kzw.core.orm.hibernate.HibernateDao;
import com.kzw.core.util.AssertUtils;

/**
 * 通过hql或sql进行单表或多表操作
 * 执行SQL或HQL语句，重写父类的方法
 * */
@SuppressWarnings({"rawtypes", "unchecked"})
@Service
public class CommonService {

	protected HibernateDao entityDao;//默认的泛型DAO成员变量.
	
	@Autowired
	public void setSessionFactory(final SessionFactory sessionFactory) {
		entityDao = new HibernateDao(sessionFactory, Object.class);
	}

	public HibernateDao getEntityDao() {
		return entityDao;
	}
	
	/**
	 * 执行HQL语句
	 * 返回更新的记录数
	 * */
	public int executeHql(String hql, Object... values) {
		return createQuery(hql, values).executeUpdate();
	}
	
	/**
	 * 执行SQL语句
	 * 返回更新的记录数
	 * */
	public int executeSql(String sql, Object... values) {
		return createSqlQuery(sql, values).executeUpdate();
	}
	
	/**
	 * 执行HQL的查询
	 * */
	public <X> List<X> queryByHql(String hql, Object... values) {
		return createQuery(hql, values).list();
	}
	
	public <X> X queryUniqueByHql(String hql, Object... values) {
		return (X)createQuery(hql, values).uniqueResult();
	}
	
	/**
	 * 执行SQL的查询
	 * */
	public <X> List<X> queryBySql(String sql, Object... values) {
		return createSqlQuery(sql, values).list();
	}
	
	/**
	 * 执行HQL查询，返回统计值(单值)
	 * */
	public Object statByHql(String hql, Object... values) {
		return createQuery(hql, values).list().get(0);
	}
	
	
	/**
	 * 执行SQL查询，返回统计值(单值)
	 * */
	public Object statBySql(String sql, Object... values) {
		return createSqlQuery(sql, values).list().get(0);
	}
	
	/**
	 * 分页查询
	 * */
	public Page pageByHql(final PageRequest request, String hql, Object... values) {
		return findPage(true, request, hql, values);
	}
	
	/**
	 * 分页查询
	 * */
	public Page pageBySql(final PageRequest request, String hql, Object... values) {
	
		return findPage(false, request, hql, values);
	}
	
	
	
	/********************************************************************/
	
	/**
	 * 根据查询HQL与参数列表创建Query对象.
	 * 与find()函数可进行更加灵活的操作.
	 * 
	 * @param values 数量可变的参数,按顺序绑定.
	 */
	protected Query createQuery(final String queryString, final Object... values) {
		AssertUtils.hasText(queryString, "queryString不能为空");
		Query query = getEntityDao().getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		query.setCacheable(true);
		return query;
	}
	
	protected Query createSqlQuery(final String queryString, final Object... values) {
		AssertUtils.hasText(queryString, "queryString不能为空");
		Query query = getEntityDao().getSession().createSQLQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		query.setCacheable(true);
		return query;
	}
	
	
	
	/**
	 * 按HQL分页查询.
	 * 
	 * @param pageRequest 分页参数.
	 * @param hql hql语句.
	 * @param values 数量可变的查询参数,按顺序绑定.
	 * 
	 * @return 分页查询结果, 附带结果列表及所有查询输入参数.
	 */
	protected Page findPage(boolean isHql, final PageRequest pageRequest, String hql, final Object... values) {
		AssertUtils.notNull(pageRequest, "pageRequest不能为空");

		Page page = new Page(pageRequest);

		if (pageRequest.isCountTotal()) {
			long totalCount = countHqlResult(hql, values);
			page.setTotalItems(totalCount);
		}

		if (pageRequest.isOrderBySetted()) {
			hql = setOrderParameterToHql(hql, pageRequest);
		}
		
		Query q = null;
		if(isHql) {
			q = createQuery(hql, values);
		} else {
			q = createSqlQuery(hql, values);
		}

		setPageParameterToQuery(q, pageRequest);

		List result = q.list();
		page.setResult(result);
		return page;
	}
	
	/**
	 * 执行count查询获得本次Hql查询所能获得的对象总数.
	 * 
	 * 本函数只能自动处理简单的hql语句,复杂的hql查询请另行编写count语句查询.
	 */
	protected long countHqlResult(final String hql, final Object... values) {
		String countHql = prepareCountHql(hql);

		try {
			Long count = findUnique(countHql, values);
			return count;
		} catch (Exception e) {
			throw new RuntimeException("hql can't be auto count, hql is:" + countHql, e);
		}
	}
	private String prepareCountHql(String orgHql) {
		String countHql = "select count (*) " + removeSelect(removeOrders(orgHql));
		return countHql;
	}
	private static String removeSelect(String hql) {
		int beginPos = hql.toLowerCase().indexOf("from");
		return hql.substring(beginPos);
	}

	private static String removeOrders(String hql) {
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}
	/**
	 * 设置分页参数到Query对象,辅助函数.
	 */
	protected Query setPageParameterToQuery(final Query q, final PageRequest pageRequest) {
		q.setFirstResult(pageRequest.getOffset());
		q.setMaxResults(pageRequest.getPageSize());
		return q;
	}
	/**
	 * 在HQL的后面添加分页参数定义的orderBy, 辅助函数.
	 */
	protected String setOrderParameterToHql(final String hql, final PageRequest pageRequest) {
		StringBuilder builder = new StringBuilder(hql);
		builder.append(" order by");

		for (Sort orderBy : pageRequest.getSort()) {
			builder.append(String.format(" %s.%s %s,", "x", orderBy.getProperty(), orderBy.getDir()));
		}

		builder.deleteCharAt(builder.length() - 1);

		return builder.toString();
	}
	/**
	 * 按HQL查询唯一对象.
	 * 
	 * @param values 数量可变的参数,按顺序绑定.
	 */
	protected <X> X findUnique(final String hql, final Object... values) {
		return (X) createQuery(hql, values).uniqueResult();
	}
}
